ADX-141: Refactor pipeline/pipelinestep CLI init commands to flag-based inputs#1010
ADX-141: Refactor pipeline/pipelinestep CLI init commands to flag-based inputs#1010
Conversation
…ag-based inputs Agent-Logs-Url: https://github.com/Clarifai/clarifai-python/sessions/56634357-d28e-490b-b85d-ec9f1907894b Co-authored-by: nitinbhojwani <9331380+nitinbhojwani@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Clarifai/clarifai-python/sessions/56634357-d28e-490b-b85d-ec9f1907894b Co-authored-by: nitinbhojwani <9331380+nitinbhojwani@users.noreply.github.com>
clarifai/cli/pipeline.py
Outdated
| # Apply defaults for unset values | ||
| effective_user_id = user_id or "your_user_id" | ||
| effective_app_id = app_id or "your_app_id" | ||
| effective_pipeline_id = ( | ||
| pipeline_id if pipeline_id and pipeline_id != _DEFAULT_PIPELINE_ID else template_name | ||
| ) |
There was a problem hiding this comment.
In template mode, effective_pipeline_id falls back to template_name whenever pipeline_id == _DEFAULT_PIPELINE_ID. Because the --pipeline_id option always supplies that default, this makes it impossible for a user to intentionally set a template-based pipeline’s ID to "hello-world-pipeline" (explicitly passing that value still gets replaced). Consider making --pipeline_id default to None and resolving defaults inside the template vs non-template branches so an explicitly provided value is always respected.
| @@ -309,17 +341,57 @@ def init(pipeline_path, template): | |||
| └── README.md # Documentation | |||
|
|
|||
| PIPELINE_PATH: Path where to create the pipeline project structure. If not specified, the current directory is used by default. | |||
|
|
|||
| Examples: | |||
|
|
|||
| # Basic initialization with defaults | |||
| clarifai pipeline init | |||
|
|
|||
| # Initialize with explicit IDs and steps | |||
| clarifai pipeline init --user_id=my_user --app_id=my_app --pipeline_id=my-pipeline --steps stepA --steps stepB | |||
|
|
|||
| # Initialize with a specific number of steps | |||
| clarifai pipeline init --user_id=my_user --app_id=my_app --pipeline_id=my-pipeline --num_steps=3 | |||
|
|
|||
| # Initialize from a template | |||
| clarifai pipeline init --template=image-classification --user_id=my_user --app_id=my_app | |||
|
|
|||
| # Initialize from a template with custom parameters | |||
| clarifai pipeline init --template=image-classification --user_id=my_user --app_id=my_app --set model_name=resnet50 | |||
| """ | |||
| # Common setup logic | |||
| pipeline_path = _prepare_pipeline_path(pipeline_path, template) | |||
| if not pipeline_path: | |||
| return # Error already shown in _prepare_pipeline_path | |||
|
|
|||
| # Resolve step names: explicit --steps take precedence, then generate from --num_steps | |||
| if steps: | |||
| resolved_steps = [*steps] | |||
| else: | |||
| default_names = ["stepA", "stepB", "stepC", "stepD", "stepE", "stepF"] | |||
| resolved_steps = [ | |||
| default_names[i] if i < len(default_names) else f"step{i + 1}" | |||
| for i in range(num_steps) | |||
| ] | |||
There was a problem hiding this comment.
--num_steps currently accepts 0 or negative values, which produces an empty resolved_steps list and generates a pipeline project with no step directories/configs. Since the generated pipeline config’s step_directories and orchestration spec are meant to reference created step folders, this can lead to a non-functional scaffold. Consider enforcing a minimum of 1 (e.g., type=click.IntRange(min=1) or a runtime validation that errors when num_steps < 1).
| else: | ||
| pipeline_step_template = get_pipeline_step_template() | ||
| with open(pipeline_step_py_path, 'w') as f: | ||
| f.write(pipeline_step_template) | ||
| logger.info(f"Created {pipeline_step_py_path}") | ||
|
|
||
| # Create requirements.txt | ||
| requirements_path = os.path.join(pipeline_step_path, "requirements.txt") | ||
| if os.path.exists(requirements_path): | ||
| logger.warning(f"File {requirements_path} already exists, skipping...") | ||
| else: | ||
| requirements_template = get_requirements_template() | ||
| with open(requirements_path, 'w') as f: | ||
| f.write(requirements_template) | ||
| logger.info(f"Created {requirements_path}") | ||
|
|
||
| # Create config.yaml | ||
| config_path = os.path.join(pipeline_step_path, "config.yaml") | ||
| if os.path.exists(config_path): | ||
| logger.warning(f"File {config_path} already exists, skipping...") | ||
| else: | ||
| config_template = get_config_template() | ||
| # Pass explicit values; get_config_template uses placeholder defaults for None | ||
| config_template = get_config_template( | ||
| **{ | ||
| k: v | ||
| for k, v in [('step_id', step_id), ('user_id', user_id), ('app_id', app_id)] | ||
| if v | ||
| } | ||
| ) | ||
| with open(config_path, 'w') as f: | ||
| f.write(config_template) | ||
| logger.info(f"Created {config_path}") |
There was a problem hiding this comment.
File writes in pipelinestep init use open(..., 'w') without an explicit encoding. Other CLI init flows in this repo write templates with encoding='utf-8' (e.g., clarifai/cli/pipeline.py:582). To avoid platform-dependent defaults and potential Unicode issues, write these files with an explicit UTF-8 encoding as well.
There was a problem hiding this comment.
There was a problem hiding this comment.
Done in commit a110b70:
- All three
open(..., 'w')calls inpipelinestep initnow useencoding='utf-8' --num_stepsnow usestype=click.IntRange(min=1), so 0 or negative values are rejected with a clear Click error message
Agent-Logs-Url: https://github.com/Clarifai/clarifai-python/sessions/e41b21f7-f0ef-47ea-a928-5e04effc1130 Co-authored-by: nitinbhojwani <9331380+nitinbhojwani@users.noreply.github.com>
Agent-Logs-Url: https://github.com/Clarifai/clarifai-python/sessions/4d08f093-dede-4f7d-ba5f-8fbb045ae478 Co-authored-by: nitinbhojwani <9331380+nitinbhojwani@users.noreply.github.com>
Minimum allowed line rate is |
… >= 1 Agent-Logs-Url: https://github.com/Clarifai/clarifai-python/sessions/dffc4e48-ca95-46f1-9a6c-a918ff4528ee Co-authored-by: nitinbhojwani <9331380+nitinbhojwani@users.noreply.github.com>
Interactive
click.prompt()calls inpipeline initandpipelinestep initcommands made them unusable in CI/CD and scripted environments. This replaces all interactive prompts with CLI flags, aligning with the model CLI interface. When flags are omitted, IDs are now resolved from the global config before falling back to an interactive prompt.Why
user_id/app_idshould be auto-detected from the active login context (~/.clarifai/config.yaml) rather than requiring explicit flags or producing unhelpful placeholder valuesHow
pipeline init— new flags replace all prompts:pipelinestep init— new flags populate config directly:Key implementation details:
_init_interactive()renamed to_init_flag_based(); allclick.prompt()calls removed from the scaffolding path_init_from_template()now acceptsuser_id,app_id,pipeline_id,override_paramsas parameters;--set key=valuereplaces template parameter promptsget_config_template()inpipeline_step_templates.pyparameterized withstep_id/user_id/app_id; TODO comments only generated when placeholder defaults are used_DEFAULT_PIPELINE_IDconstant extracted to avoid coupling between decorator default and runtime logiclist(steps)was calling the CLIlistcommand (which shadows the built-in); changed to[*steps]resolve_id()utility inclarifai/utils/cli.py: resolves a value from CLI flag → global config current context → interactive prompt. Used foruser_idandapp_idin bothpipeline initandpipelinestep init--num_stepsnow usestype=click.IntRange(min=1), rejecting 0 or negative values before any scaffold logic runsopen(..., 'w')calls inpipelinestep initnow includeencoding='utf-8'to avoid platform-dependent encoding issuesTests
input=strings andclick.promptmockstest_init_command_without_template_calls_interactiverenamed totest_init_command_without_template_calls_flag_based--user_id/--app_idflags (matching the new resolution behavior)test_init_with_template_optionandtest_init_without_template_optionupdated to assert the full argument list passed to_init_from_templateand_init_flag_based(prepared path, template name,user_id,app_id,pipeline_id,override_params/step_names)Notes
--user_id/--app_idare omitted and the global config has no values, the user is prompted interactively — making the command safe in both scripted and interactive shells--num_steps Nis available as a shorthand when step names don't matter (defaults to 2, minimum 1)step_idinpipelinestep initremains optional; a# TODO: please fill incomment is generated inconfig.yamlwhen it is not provided